Repository navigation
ci(release): decide changeset coverage from turbo's build hashes - #6
Merged
Merged
Conversation
The release gate moves from `paths` to `{ strategy: turbo, task: build }`.
`main` has carried turbo.json since b7e907a, so the comment saying the turbo
gate refuses this repository's base is stale. A publishable package now needs
an intent when its turbo `build` hash moves between base and head
(pnpm-release-management b9b4a1d change-verdict.ts verdictTouched), the gate
systemfsoftware and pnpm-release-management use. .changeset/README.md states
the same rule and keys the 0.0.0 `none` caveat on the tarball.
This was referenced Oct 13, 2026
The turbo gate asks for an intent when a package's `build` hash moves, and files outside the package can move it. turbo 2.10.12's dry run moved @systemfsoftware/api-extractor#build on a change to turbo.json's `build` task, to `globalEnv`, and to the root package.json's dependencies, and not on root metadata, a lockfile comment or another task's settings. The README now names those inputs, so a contributor can see why such a pull request owes an intent (`none` when the tarball is unchanged).
The list of outside-package inputs that move a package's `build` hash named only the three that turbo 2.10.12's dry run was measured on, but read as complete. Review named other inputs turbo also folds in (lockfile resolutions, other root dependency fields). The sentence now gives the measured inputs as examples; the gate itself compares turbo's hashes.
systemfsoftware-maker
added a commit
that referenced
this pull request
Oct 13, 2026
…ancestry; profile harness maps .cts and counts missing emits (U13 #5, #6) The derived tsconfig sat in a bare temp directory, so tsc looked up a `types` entry naming a package (`vitest/globals`) in node_modules directories above the temp dir and refused with TS2688. Four corpus packages the earlier profile modelled were refused this way. The derived config is now written into a scoped mirror of the package config's directory, each ancestor carrying a symlink to the real node_modules, so tsc's upward walk matches the package's own. The explicit typeRoots chain this replaces is gone; derivedTsconfig becomes writeDerivedTsconfig, which writes the mirror and returns the config path. e2e: the equality journey gains a package whose tsconfig names `types: ["greeting/globals"]` from its own node_modules through an exports subpath. It fails at c4202d9 with TS2688 and passes after. Profile harness: .cts sources map to .d.cts and are discovered; null and array export targets are skipped; a file missing from the 5.9.3 output is counted apart from a printing difference; tsc's stderr is drained alongside stdout.
systemfsoftware-maker
added a commit
that referenced
this pull request
Oct 13, 2026
…mirrored node_modules learning Supersedes the 1834 plan. U13 rules every finding of the full-path review at e5ec09d: six fixed (#1, #2, #4, #5, #6, #8), the mapped-type/infer anchor-50 note fixed after reproduction, the TS2688 regression the rerun found fixed, #3 and #7 rejected with the validator's reasons, and the Diagnostic.code refusal note made a PR4 follow-up. KTD3 now describes the mirrored node_modules ancestry instead of the explicit typeRoots chain, and the policy gains isolatedDeclarations: false. docs/profiles/2026-10-13-front-end-phases: the U13 harness at 432b608 on the corpus at 732f66a0c8. 126 of 585 shared declaration files print differently between TypeScript 7 and 5.9.3 and none are missing; every package's route and outcome match the second profile. The emit-option learning gains the isolatedDeclarations case and the types-from-node_modules case, each with its gate.
ryanleecode
approved these changes
Oct 13, 2026
ryanleecode
left a comment
Contributor
There was a problem hiding this comment.
Root verdict at ca70cc2: 12/12 green, 0 behind, 0 threads; exact-head DeepSeek receipt with 5 roots at 0923912eed; the light-checks-run-the-pinned-tool breach at release.jsonc:13 is the root-ruled declared departure (D1=a, upstream cure commissioned to pnpm-release-management); every finding dispositioned; ce-compound evaluated; privacy clean. Retires the banned paths classifier for turbo's build-hash gate.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The Changeset Check now decides which packages need an intent by turbo
buildhash, not by path.release.jsoncchanges from{ "strategy": "paths" }to{ "strategy": "turbo", "task": "build" }. pnpm-release-management is pinned atb9b4a1d. At that pin, a publishable package needs an intent when itsbuildtask hash differs between the base and the head (process-adapter/src/change-verdict.tsverdictTouched, evidence fromturbo-evidence.ts). systemfsoftware and pnpm-release-management already use the same gate.The old comment said the turbo gate dry-runs turbo at the base and refuses a base without
turbo.json. That is out of date:mainhas carriedturbo.jsonsinceb7e907a(#2)..changeset/README.mdnow states the turbo rule. Its 0.0.0 caveat now keys on a change to the tarball rather than to thepackages/api-extractorpath..changeset/README.md:12-15also says that files outside the package can move the hash, with measured examples (below), and that such a pull request names the package, withnonewhen its tarball does not change.No package, app or intent changes, and the shipped tarball is unchanged.
Turbo evidence (turbo 2.10.12, the catalog pin)
turbo query: 3 packages (//,@systemfsoftware/api-extractor,api-extractor-e2e). One graph edge: e2e → api-extractor,development. Boundaries: 0 diagnostics.affectedTasks(tasks: ["build"])over this repo's merged PRs: build: move api-extractor onto the systemfsoftware family toolchain #2 → bothbuildtasks (TaskGlobalFileChanged turbo.json); fix(release): give main's Release the version surface it needs #3, chore(release): version packages #4 and ci(mutation): run mutation testing on main through systemfsoftware's reusable workflow #5's head → none.Outside-package inputs, measured with
turbo run build --dry=jsonon a scratch copy of this tree (@systemfsoftware/api-extractor#build, base hash345f4274c3eb009e):turbo.jsontasks.build.outputs(7b29fe4c…),globalEnvadded (c74adef1…), a rootpackage.jsondevDependency (dfc029a9…).package.jsondescription, a comment line inpnpm-lock.yaml,turbo.jsontasks.lint.buildscript deleted, turbo kept the task (command<NONEXISTENT>) and its hash moved (873e9aab…), so the check still asks for an intent.Gate proof
Each run is
changeset-management check origin/main, using the release tools from this repo's dev shell (theb9b4a1dpin), against base9bd7396, which hasturbo.json. Each control was a throwaway local commit on top of this branch. The controls are deleted, and no base worktree was left behind.main)packages/api-extractor/src/mod.ts@systemfsoftware/api-extractorturbo.jsonbuild.envgains a variableapps/api-extractor-e2e/outputs.ts(private)packages/api-extractor/stryker.config.tsonly (not infiles)Under turbo
build, any file in the package's default inputs counts as a change. E shows the cost: a change outside the tarball still needs an intent, and its intent isnone. The paths gate behaves the same here, so this is not a regression.CI on this head is the proof that the gate runs inside the
devshell: truesandbox. No sibling caller runs turbo with devshell today: stryker-js-effect uses devshell withpaths.Credentials
The gate runs
turbo run build --dry=jsonwith noTURBO_TOKEN/TURBO_TEAM.ci.ymlandchangeset-check.ymlpass neither, so turbo uses only the local cache and a public-repo run gets no credentials.Code review (full path, report-only, final head
ca70cc2)ce-code-review mode:agent base:origin/main depth:fullran against headca70cc232bbe8abb486862e4cb8e8356f8473c7a, base6d9d72bb9554746fd798353aefb6ddbb5885b07e(mainafter #11). Run20261013-202050-2791c2b1, an exact-head delta round after the merge ofmainbelow. CE verdict: Ready to merge. The review has no actionable finding. The one pack-rule breach is a departure the root accepted (below), and the PR waits for the root's verdict; it is not merged.Rounds on this PR:
da6d7f2: full path, run…856eb364.a2aa2cd: plain merge oforigin/main(cd9f133, chore(packs): enroll ci-wall-clock and repin compound packs to 8d07118079 #8). The merge changes only.compound-engineering/config.yaml, which pins8d071180798f9ea82ef5a4fd877152dacc02f6b6and enrollsci-wall-clock; nothing was rebased or force-pushed. Full-path delta round, run…d26930a4.12e09fe: README fix. Round…3e69dc8eis superseded, because its two findings were fixed in the next commit.0a72fe4: full path, run…84707681, Ready to merge.bd4f9c9: plain merge oforigin/main(a053ee6, test(api-extractor): retire the generated schema laws #9), with no rebase and no force-push. The merge brings exactly test(api-extractor): retire the generated schema laws #9's five files:.changeset/orange-bears-rush.md, the rootpackage.jsondevDependency,pnpm-lock.yaml,packages/api-extractor/vitest.config.ts, and the deletedsrc/schema-laws.test.ts.git show --remerge-diffof the merge is empty, and this PR's own diff againstmainis unchanged (release.jsonc,.changeset/README.md). Exact-head delta round, run…6a9431bc: Ready to merge.ca70cc2: plain merge oforigin/main(6d9d72b, chore(packs): repin compound packs to systemfsoftware 0923912eed (#761) #11), with no rebase and no force-push. The merge brings one line,.compound-engineering/config.yaml:3, which now pins the packs to0923912eedb46b34645a22d5d67f18b442c37919.git show --remerge-diffis empty, and this PR's diff againstmainis unchanged. Because the pack pin moved, this round judges the PR against the new rule set. Exact-head delta round: this receipt.Packs: resolved from the
ca70cc2head's own.compound-engineering/config.yaml.--declared-onlyand the full resolve both exit 0 with no errors or warnings, giving five roots at0923912eedb46b34645a22d5d67f18b442c37919. The packs hold 52 rule files:cell-architecture15,boundary-testing7,schema-laws9,package-topology7,ci-wall-clock14. Applicability was decided by meaning (CONST-T12), and systemfsoftwareCONSTITUTION.md(unchanged by the repin) outranks pack rules (CONST-S2).Reviewers (
ca70cc2round): correctness, project standards (AGENTS.mdplus the constitution), learnings with the per-rule receipt, reliability, security, and adversarial. All six ran as omp agents. Their session logs show onlydeepseek-v4.1-flashturns, with no Claude, Grok or xAI turn. The permitted non-Claude peer is omp'sadversarial-reviewerondeepseek-v4.1-flash(21 turns); no external peer CLI is installed. No finding needed validation this round. In thebd4f9c9round the validator ran on the same model (17 turns). No reviewer timed out: the longest slice took 6 m 16 s.Declared breach:
ci-wall-clock/light-checks-run-the-pinned-tool(accepted by root ruling)ci-wall-clock/light-checks-run-the-pinned-tool, unchanged between the old pin8d07118and the new pin0923912eed. The case is its "changeset check" case: this diff changes what the changeset check runs, which brings a workspace install into it. The rule: a changeset check runs its pinned tool directly, with no workspace install, dev shell or producer job before it, and the tool comes from one pinned fetch.release.jsonc:13("gate": { "strategy": "turbo", "task": "build" }) makes the check take turbo from the workspace install. pnpm-release-managementb9b4a1dpackages/process-adapter/src/turbo-evidence.ts:27readsTURBO_BIN = 'node_modules/.bin/turbo', and:106-116refuses withTurboPinUnusablewhen it is absent. So this check can never run install-free; thepathsstrategy needed only git.devshell: trueat.github/workflows/changeset-check.yml:17and repins pnpm-release-management, so the check runs install-free. Neither change belongs in this PR.changeset-check.yml:17passesdevshell: true, so the job already runs the dev shell and this repository'sbootstrapbefore the verdict, onmainas well. Measured: the check job took 7 m 44 s onda6d7f2(run 38298009147) and 8 m 28 s on ci(mutation): run mutation testing on main through systemfsoftware's reusable workflow #5's head with thepathsgate (run 38291945550); it took 6 m 39 s on0a72fe4(run 38376783369) and 7 m 13 s onbd4f9c9(run 38450595183). Onca70cc2(run 38462036008) the job took 17 m 02 s. From the log: building the pinnedrelease-toolstook 8 m 45 s and the dev-shell bootstrap took 6 m 10 s. The turbo verdict step took 12.6 s. Both slow steps run under thepathsgate too. The bootstrap dominates every one of these runs, so the switch adds no measured wall time today. The breach is structural.pathsis closed: upstream #56 (merged as2b89555) refuses the paths gate where turbo drives the comparison.Findings and dispositions
ca70cc2release.jsonc:13ca70cc2ci-wall-clock/local-gate-equals-pr-gate(new at the pin): the changeset check is not inAGENTS.md's pre-push commands (adversarial, noted without a finding).AGENTS.mdalready documents.bd4f9c9release.jsonc:13bd4f9c9release.jsonc:13changeset-check.yml:17(devshell: true) already runs the pull request's own bootstrap before the verdict onmain, and the release tools come from the pinned workflow, not the pull request. The workspace-turbo consequence is the D1 departure, cured by unit R.bd4f9c9.changeset/README.md:12orange-bears-rush.mdnow onmain, satisfies the gate for a later pull request that moves the same package (adversarial).gate-changes.ts:79-82store.listIntents()), sopathsbehaves the same onmain. A freshness guard that counts only intents the pull request adds would belong to pnpm-release-management. It is recorded here for that owner and not commissioned by this PR.bd4f9c9release.jsonc:1312e09ferow below.0a72fe4.changeset/README.md:15noneintent is the author's judgement.nonewithtag-integrity-mismatch, and the README's 0.0.0 bullet says so. Guidance in the refusal text belongs to pnpm-release-management (gate-changes.ts:104-118).a2aa2cd.changeset/README.md:1012e09feand0a72fe4:README.md:12-15names measured examples.a2aa2cdrelease.jsonc:13a2aa2cdrelease.jsonc:13buildtask that vanishes reads as unchanged (change-verdict.ts:35,54).<NONEXISTENT>and its hash moved.12e09fe.changeset/README.md:130a72fe4: the inputs are now given as examples.12e09ferelease.jsonc:13timeout-minuteson such a job. The bound belongs in pnpm-release-management'schangeset-check.ymlandProcessRun.ts.da6d7f2release.jsonc:13da6d7f2:244paths.Pack receipt (
ca70cc2, packs at0923912eed)ci-wall-clock/light-checks-run-the-pinned-toolrelease.jsonc:13ci-wall-clock/pr-gate-runs-only-what-the-diff-affectsbuildgraph instead of a hand-kept path list, and this receipt satisfies the rule's full-path review clauserelease.jsonc:13;.changeset/README.md:10-15Not matched (50), each named in the learnings receipt and judged by meaning:
ci-wall-clock(12):failures-name-the-gate-and-location(new),flake-outputs-build-once-on-main,gates-share-one-job,heavy-lanes-state-their-daily-minutes,heavy-suites-judge-every-main-commit,local-gate-equals-pr-gate(new; ruled above),no-dev-shell-on-verdict-lanes(it covers lint, typecheck and test jobs),no-lane-grades-the-repos-own-docs,one-stuck-job-backstop,red-main-reverts-its-culprit-same-day,release-promotes-the-staging-proven-build,setup-under-a-minute.boundary-testing(7, including the newexpected-values-never-come-from-the-subjects-output),cell-architecture(15),schema-laws(9) andpackage-topology(7): every rule. The diff changes no code, test, schema, entry point, port, adapter or published surface.The
devshell: trueatchangeset-check.yml:17predates this diff.light-checks-run-the-pinned-toolandsetup-under-a-minuteboth refuse that shape on their own, and the unit R follow-up removes it.Other items and their dispositions
:244(this PR'sda6d7f2finding 3) and:446(#8's finding 5)supersedes:. It does not block this PR.turbo.jsonchange tobuild.inputscould narrow what the gate seesturbo.jsondeclares nobuild.inputstoday. Any such change is itself a global input that moves every package's hash, so the gate asks for intents on that pull request and its review decides it.pathsgate until they mergemainrelease.jsoncfrom its own head. #7 and #5 get the turbo gate at their next merge ofmain.Checks at
ca70cc2pnpm run bootstrapexit 0.changeset-management check origin/mainexit 0: "changeset gate: no publishable package changed (2 member(s))".pnpm lintandpnpm typecheckexit 0.turbo run test --forceexit 0 withCached: 0 cached, 2 total(api-extractor 5 files and 103 tests; e2e 1 of 1).--declared-onlyand full exit 0, errors and warnings none, five roots at0923912eed.bd4f9c9(CI 38450595050, Changeset Check 38450595183) and0a72fe4(CI 38376783225, Changeset Check 38376783369), all green.ce-compound
ce-compoundwas evaluated for this PR (mode non-interactive) and endedDocumentation skipped. Re-evaluated atbd4f9c9andca70cc2: neither merge adds a new lesson, and the skip stands: the lesson worth keeping, install-free light checks versus turbo-native change evidence, waits on unit R's upstream fix.Privacy
Grep at
ca70cc2over this PR's diff againstmain, both merge commit messages and this body: the 54 private systemfsoftware repository names, the required literal termsyounna,younna-ai,Stead,wireblast(whole word, case-insensitive), host paths, and credential or remote-cache token names. Hits: none in the diff or the merge commit message. In this body, the only hits are this paragraph's own list, the file nameAGENTS.md, the common word "global" (in "global input"), andTURBO_TOKEN/TURBO_TEAM, which the Credentials section names because the gate runs without them.